package sole.hawking.image.test;

import java.awt.Point;
import java.util.Iterator;
import java.util.Vector;

/**
 * Erode is an algorithm to erode a binary image using a 3x3 kernel.
 * 
 * @author Simon Horne
 * @author Craig Strachan
 * @author Judy Robertson, SELLIC Online
 */
public class ErodeFilter extends Thread {

    /**
     * Default no-args constructor.
     */
    public ErodeFilter() {
    }

    /**
     * Returns true if the kernel matches the area of image centred on the given
     * point.
     * 
     * @param p
     *            The centre point identifying the pixel neighbourhood.
     * @param pixels
     *            The 2D array representing the image.
     * @param w
     *            The width of the image.
     * @param h
     *            The height of the image.
     * @param kernel
     *            The array representing the kernel.
     * @return True or false (true - the kernel and image match).
     */
    public static boolean kernelMatch(Point p, int[][] pixels, int w, int h,
            int[] kernel) {
        
        for (int j = -1; j < 2; ++j) {
            for (int i = -1; i < 2; ++i) {
                if (kernel[((j + 1) * 3) + (i + 1)] == 1) {
                    if ((p.x + i >= 0) && (p.x + i < w) && (p.y + j >= 0)
                            && (p.y + j < h)) {
                        if (pixels[p.x + i][p.y + j] == BinaryFast.background) {
                            return true;
                        }
                    }
                }
            }
        }
        return false;
    }

    /**
     * Returns true if the kernel consists of 9 1s.
     * 
     * @param kernel
     *            The array representing the kernel.
     * @return True or false (true - kernel is all 1s).
     */

    public static boolean kernelAll1s(int[] kernel) {
        for (int i = 0; i < 9; ++i) {
            if (kernel[i] == 0)
                return false;
        }
        return true;
    }

    /**
     * Applies a single iteration of the erode algorithm to the image.
     * 
     * @param binary
     *            The BinaryFast representation of the input image.
     * @param kernel
     *            The array representing the kernel.
     * @return The BinaryFast representation of the new eroded image.
     */
    public static BinaryFast erodeSingleIteration(BinaryFast binary,
            int[] kernel) {
        Vector result = new Vector();
        Iterator it;
        Point p;

        it = binary.foregroundEdgePixels.iterator();
        if (!kernelAll1s(kernel)) {
            while (it.hasNext()) {
                p = new Point((Point) it.next());
                if (kernelMatch(p, binary.pixels, binary.w, binary.h, kernel)) {
                    binary.backgroundEdgePixels.add(p);
                    result.add(p);
                }
            }
        } else {

            while (it.hasNext()) {
                p = new Point((Point) it.next());
                binary.backgroundEdgePixels.add(p);
                result.add(p);
            }
        }
        it = result.iterator();
        while (it.hasNext()) {
            binary.removePixel((Point) it.next());
        }
        binary.generateForegroundEdgeFromBackgroundEdge();
        return binary;
    }

    /**
     * Applies several iterations of the erode algorithm to an image.
     * 
     * @param binary
     *            The BinaryFast representation of the input image.
     * @param kernel
     *            The array representing the kernel.
     * @param iterations
     *            The number of iterations to be applied.
     * @return The BinaryFast representation of the new eroded image.
     */
    public static BinaryFast erode_image(BinaryFast binary, int[] kernel,
            int iterations) {
        int i = 0;
        kernel[4] = 1;// Ignore centre pixel value in kernel (stops whiteout)
        while (i < iterations) {
            binary = erodeSingleIteration(binary, kernel);
            ++i;
        }
        binary.generateBackgroundEdgeFromForegroundEdge();
        return binary;
    }

}
